#pragma once
#define _SILENCE_EXPERIMENTAL_FILESYSTEM_DEPRECATION_WARNING
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include <sys/stat.h>
#include <experimental/filesystem>
#include <unordered_map>



namespace cloud
{
    namespace fs = std::experimental::filesystem;
    class FileUtil
    {
    private:
        std::string _filename;
    public:
        FileUtil(const std::string& filename)
            :_filename(filename)
        {} // structure function 

        bool Remove()
        {
            remove(_filename.c_str());
            return true;
        }
        int64_t FileSize()
        {
            struct stat st;
            int ret = stat(_filename.c_str(), &st);
            if (ret < 0)
            {
                std::cout << "Get file size failed!" << std::endl;
                return -1;
            }

            return st.st_size;
        }

        time_t LastModifyTime()
        {
            struct stat st;
            int ret = stat(_filename.c_str(), &st);
            if (ret < 0)
            {
                std::cout << "Get lastmodifytime failed!" << std::endl;
                return -1;
            }

            return st.st_mtime;
        }


        time_t LastAccessTime()
        {
            struct stat st;

            int ret = stat(_filename.c_str(), &st);
            if (ret < 0)
            {
                std::cout << "Get lastaccesstime failed!" << std::endl;
                return -1;
            }

            return st.st_atime;
        }

        std::string FileName()
        {
            std::experimental::filesystem::path p(_filename);
            return p.filename().string();
        }

        bool GetPosLen(std::string& body, size_t pos, size_t len)
        {
            size_t fsize = this->FileSize();
            if (pos + len < fsize)
            {
                std::cout << "Get file len failed!" << std::endl;
                return false;
            }

            std::ifstream ifs;
            ifs.open(_filename.c_str(), std::ios::binary);
            if (ifs.is_open() == false)
            {
                std::cout << "open file failed!" << std::endl;
                return false;
            }

            ifs.seekg(pos, std::ios::beg);
            body.resize(len);
            ifs.read(&body[0], len);
            if (ifs.good() == false)
            {
                std::cout << "read file failed!" << std::endl;
                return false;
            }
            ifs.close();
            return true;
        }

        bool GetContent(std::string& body)
        {
            size_t fsize = this->FileSize();
            return this->GetPosLen(body, 0, fsize);
        }

        bool SetContent(const std::string& body)
        {
            std::ofstream ofs;
            ofs.open(_filename.c_str(), std::ios::binary);
            if (ofs.is_open() == false)
            {
                std::cout << "open ofs failed" << std::endl;
                return false;
            }

            ofs.write(&body[0], body.size());
            if (ofs.good() == false)
            {
                std::cout << "ofs write failed!" << std::endl;
                return false;
            }
            ofs.close();
            return true;
        }

        bool Exists()
        {
            return fs::exists(_filename);
        }

        bool CreateDirectory()
        {
            if (this->Exists())
            {
                return true;
            }

            return fs::create_directories(_filename);
        }

        bool ScanDirectory(std::vector<std::string>& arry)
        {
            this->CreateDirectory();
            for (auto& p : fs::directory_iterator(_filename))
            {
                if (fs::is_directory(p) == true)
                {
                    continue;
                }

                // return relative path
                arry.push_back(fs::path(p).relative_path().string());
            }
            return true;
        }
    };

}
